/**
 * Project Name:SimonFramework
 * File Name:ImageLoaderConfiguration.java
 * Package Name:com.cmonbaby.images.core
 * Date:2016-5-19 下午3:47:04
 * Copyright (c) 2016, simon@cmonbaby.com All Rights Reserved.
 */
package com.cmonbaby.images.core;

import android.content.Context;
import android.content.res.Resources;
import android.util.DisplayMetrics;

import com.cmonbaby.images.cache.disc.DiskCache;
import com.cmonbaby.images.cache.disc.naming.FileNameGenerator;
import com.cmonbaby.images.cache.memory.MemoryCache;
import com.cmonbaby.images.cache.memory.impl.FuzzyKeyMemoryCache;
import com.cmonbaby.images.core.assist.FlushedInputStream;
import com.cmonbaby.images.core.assist.ImageSize;
import com.cmonbaby.images.core.assist.QueueProcessingType;
import com.cmonbaby.images.core.decode.ImageDecoder;
import com.cmonbaby.images.core.download.ImageDownloader;
import com.cmonbaby.images.core.process.BitmapProcessor;
import com.cmonbaby.images.utils.Cons;
import com.cmonbaby.images.utils.LogUtils;
import com.cmonbaby.images.utils.MemoryCacheUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Executor;

/**
 * <p>Author:      Simon
 * <p>QO:          8950764
 * <p>Email:       simon@cmonbaby.com
 * <p>WebSize:     https://www.cmonbaby.com
 * <p>Version:     1.0.0
 * <p>Date:        2020/12/29
 * <p>Description: 本配置用于ImageLoader
 */
public final class ImageLoaderConfiguration {

    final Resources resources; // 资源

    final int maxImageWidthForMemoryCache; // 内存缓存图片最大宽度
    final int maxImageHeightForMemoryCache; // 内存缓存图片最大高度
    final int maxImageWidthForDiskCache; // 本地缓存图片最大宽度
    final int maxImageHeightForDiskCache; // 本地缓存图片最大高度
    final BitmapProcessor processorForDiskCache; // 本地缓存处理器

    final Executor taskExecutor; // 任务执行器
    final Executor taskExecutorForCachedImages; // 图片缓存任务执行器
    final boolean customExecutor; // 自定义任务执行器
    final boolean customExecutorForCachedImages; // 自定义图片缓存任务执行器

    final int threadPoolSize; // 线程池大小
    final int threadPriority; // 线程优先级
    final QueueProcessingType tasksProcessingType; // 任务进程方式

    final MemoryCache memoryCache; // 内存缓存
    final DiskCache diskCache; // 本地缓存
    final ImageDownloader downloader; // 图片下载器
    final ImageDecoder decoder; // 图片解码器
    final DisplayImageOptions defaultDisplayImageOptions; // 默认图片显示设置

    final ImageDownloader networkDeniedDownloader; // 网络访问拒绝下载器
    final ImageDownloader slowNetworkDownloader; // 网络缓慢下载器

    private ImageLoaderConfiguration(final Builder builder) {
        resources = builder.context.getResources();
        maxImageWidthForMemoryCache = builder.maxImageWidthForMemoryCache;
        maxImageHeightForMemoryCache = builder.maxImageHeightForMemoryCache;
        maxImageWidthForDiskCache = builder.maxImageWidthForDiskCache;
        maxImageHeightForDiskCache = builder.maxImageHeightForDiskCache;
        processorForDiskCache = builder.processorForDiskCache;
        taskExecutor = builder.taskExecutor;
        taskExecutorForCachedImages = builder.taskExecutorForCachedImages;
        threadPoolSize = builder.threadPoolSize;
        threadPriority = builder.threadPriority;
        tasksProcessingType = builder.tasksProcessingType;
        diskCache = builder.diskCache;
        memoryCache = builder.memoryCache;
        defaultDisplayImageOptions = builder.defaultDisplayImageOptions;
        downloader = builder.downloader;
        decoder = builder.decoder;

        customExecutor = builder.customExecutor;
        customExecutorForCachedImages = builder.customExecutorForCachedImages;

        networkDeniedDownloader = new NetworkDeniedImageDownloader(downloader);
        slowNetworkDownloader = new SlowNetworkImageDownloader(downloader);

        LogUtils.e(Cons.LOG_TAG, builder.writeLogs + "");
    }

    /**
     * 创建默认配置为 {@link ImageLoader} <br />
     * <b>默认值:</b>
     * <ul>
     * <li>maxImageWidthForMemoryCache = 设备的屏幕宽度</li>
     * <li>maxImageHeightForMemoryCache = 设备的屏幕高度</li>
     * <li>maxImageWidthForDikcCache = 无限制</li>
     * <li>maxImageHeightForDiskCache = 无限制</li>
     * <li>threadPoolSize = {@link Builder#DEFAULT_THREAD_POOL_SIZE this}</li>
     * <li>threadPriority = {@link Builder#DEFAULT_THREAD_PRIORITY this}</li>
     * <li>允许在内存中缓存不同大小的图像</li>
     * <li>memoryCache = {@link DefaultConfigurationFactory#createMemoryCache(Context, int)}</li>
     * <li>diskCache = {@link com.cmonbaby.images.cache.disc.impl.UnlimitedDiskCache}</li>
     * <li>imageDownloader = {@link DefaultConfigurationFactory#createImageDownloader(Context)}</li>
     * <li>imageDecoder = {@link DefaultConfigurationFactory#createImageDecoder(boolean)}</li>
     * <li>diskCacheFileNameGenerator = {@link DefaultConfigurationFactory#createFileNameGenerator()}</li>
     * <li>defaultDisplayImageOptions = {@link DisplayImageOptions#createSimple() Simple options}</li>
     * <li>tasksProcessingOrder = {@link QueueProcessingType#FIFO}</li>
     * <li>禁用详细的日志记录</li>
     * </ul>
     */
    public static ImageLoaderConfiguration createDefault(Context context) {
        return new Builder(context).build();
    }

    ImageSize getMaxImageSize() {
        DisplayMetrics displayMetrics = resources.getDisplayMetrics();

        int width = maxImageWidthForMemoryCache;
        if (width <= 0) {
            width = displayMetrics.widthPixels;
        }
        int height = maxImageHeightForMemoryCache;
        if (height <= 0) {
            height = displayMetrics.heightPixels;
        }
        return new ImageSize(width, height);
    }

    /**
     * 为 {@link ImageLoaderConfiguration} 构造
     */
    public static class Builder {

        private static final String WARNING_OVERLAP_DISK_CACHE_PARAMS = "diskCache(), diskCacheSize() and diskCacheFileCount calls overlap each other";
        private static final String WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR = "diskCache() and diskCacheFileNameGenerator() calls overlap each other";
        private static final String WARNING_OVERLAP_MEMORY_CACHE = "memoryCache() and memoryCacheSize() calls overlap each other";
        private static final String WARNING_OVERLAP_EXECUTOR = "threadPoolSize(), threadPriority() and tasksProcessingOrder() calls "
                + "can overlap taskExecutor() and taskExecutorForCachedImages() calls.";

        /** {@value} */
        public static final int DEFAULT_THREAD_POOL_SIZE = 3;
        /** {@value} */
        public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 2;
        /** {@value} */
        public static final QueueProcessingType DEFAULT_TASK_PROCESSING_TYPE = QueueProcessingType.FIFO;

        private Context context;

        private int maxImageWidthForMemoryCache = 0;
        private int maxImageHeightForMemoryCache = 0;
        private int maxImageWidthForDiskCache = 0;
        private int maxImageHeightForDiskCache = 0;
        private BitmapProcessor processorForDiskCache = null;

        private Executor taskExecutor = null;
        private Executor taskExecutorForCachedImages = null;
        private boolean customExecutor = false;
        private boolean customExecutorForCachedImages = false;

        private int threadPoolSize = DEFAULT_THREAD_POOL_SIZE;
        private int threadPriority = DEFAULT_THREAD_PRIORITY;
        private boolean denyCacheImageMultipleSizesInMemory = false;
        private QueueProcessingType tasksProcessingType = DEFAULT_TASK_PROCESSING_TYPE;

        private int memoryCacheSize = 0;
        private long diskCacheSize = 0;
        private int diskCacheFileCount = 0;

        private MemoryCache memoryCache = null;
        private DiskCache diskCache = null;
        private FileNameGenerator diskCacheFileNameGenerator = null;
        private ImageDownloader downloader = null;
        private ImageDecoder decoder;
        private DisplayImageOptions defaultDisplayImageOptions = null;

        private boolean writeLogs = false;

        public Builder(Context context) {
            this.context = context.getApplicationContext();
        }

        /**
         * 内存缓存的设置选项
         *
         * @param maxImageWidthForMemoryCache  最大图像宽度在解码一个图像的时候将用于内存保存 {@link android.graphics.Bitmap Bitmap}. <b>默认值 - 设备屏幕宽度</b>
         * @param maxImageHeightForMemoryCache 最大图像高度在解码一个图像的时候将用于内存保存 {@link android.graphics.Bitmap Bitmap}. <b>默认值 - 设备屏幕高度</b>
         */
        public Builder memoryCacheExtraOptions(int maxImageWidthForMemoryCache, int maxImageHeightForMemoryCache) {
            this.maxImageWidthForMemoryCache = maxImageWidthForMemoryCache;
            this.maxImageHeightForMemoryCache = maxImageHeightForMemoryCache;
            return this;
        }

        /**
         * @deprecated 使用
         * {@link #diskCacheExtraOptions(int, int, com.cmonbaby.images.core.process.BitmapProcessor)}
         * instead
         */
        @Deprecated
        public Builder discCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache,
                                             BitmapProcessor processorForDiskCache) {
            return diskCacheExtraOptions(maxImageWidthForDiskCache, maxImageHeightForDiskCache, processorForDiskCache);
        }

        /**
         * 之前设置选项调整/压缩下载的图像将保存到磁盘缓存<br />
         * <b>注意: 使用这个选项只有当你有适当的需求。它可以使ImageLoader变慢</b>
         *
         * @param maxImageWidthForDiskCache  最大宽度的下载图片保存在磁盘缓存
         * @param maxImageHeightForDiskCache 最大高度的下载图片保存在磁盘缓存
         * @param processorForDiskCache      null-ok; {@linkplain BitmapProcessor Bitmap processor} 之前处理图像保存在磁盘缓存
         */
        public Builder diskCacheExtraOptions(int maxImageWidthForDiskCache, int maxImageHeightForDiskCache,
                                             BitmapProcessor processorForDiskCache) {
            this.maxImageWidthForDiskCache = maxImageWidthForDiskCache;
            this.maxImageHeightForDiskCache = maxImageHeightForDiskCache;
            this.processorForDiskCache = processorForDiskCache;
            return this;
        }

        /**
         * 设置自定义 {@linkplain Executor executor} 任务加载和显示图像<br />
         * <br />
         * <b>注意:</b> 如果你设置自定义执行，那么以下配置选项将不考虑
         * 执行者:
         * <ul>
         * <li>{@link #threadPoolSize(int)}</li>
         * <li>{@link #threadPriority(int)}</li>
         * <li>{@link #tasksProcessingOrder(QueueProcessingType)}</li>
         * </ul>
         *
         * @see #taskExecutorForCachedImages(Executor)
         */
        public Builder taskExecutor(Executor executor) {
            if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_EXECUTOR);
            }

            this.taskExecutor = executor;
            return this;
        }

        /**
         * 设置自定义 {@linkplain Executor executor} 显示的任务 <b>缓存在磁盘上的</b> 图片 (这些任务执行快速UIL更偏向使用单独的执行者)<br />
         * <br />
         * 如果你设置相同的执行者 {@linkplain #taskExecutor(Executor) general tasks} 并且
         * 任务对缓存的图片(此方法),那么这些任务将在相同的线程池. 所以短暂的任务可以等待很长时间.<br />
         * <br />
         * <b>NOTE:</b> 如果你设置自定义执行者,那么以下配置选项将不考虑
         * 执行者:
         * <ul>
         * <li>{@link #threadPoolSize(int)}</li>
         * <li>{@link #threadPriority(int)}</li>
         * <li>{@link #tasksProcessingOrder(QueueProcessingType)}</li>
         * </ul>
         *
         * @see #taskExecutor(Executor)
         */
        public Builder taskExecutorForCachedImages(Executor executorForCachedImages) {
            if (threadPoolSize != DEFAULT_THREAD_POOL_SIZE || threadPriority != DEFAULT_THREAD_PRIORITY || tasksProcessingType != DEFAULT_TASK_PROCESSING_TYPE) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_EXECUTOR);
            }

            this.taskExecutorForCachedImages = executorForCachedImages;
            return this;
        }

        /**
         * 设置图像显示任务的线程池的大小<br />
         * 默认值 - {@link #DEFAULT_THREAD_POOL_SIZE this}
         */
        public Builder threadPoolSize(int threadPoolSize) {
            if (taskExecutor != null || taskExecutorForCachedImages != null) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_EXECUTOR);
            }

            this.threadPoolSize = threadPoolSize;
            return this;
        }

        /**
         * 设置图像加载线程的优先级。应该不大于 {@link Thread#MAX_PRIORITY} 或者
         * 不到 {@link Thread#MIN_PRIORITY}<br />
         * 默认值 - {@link #DEFAULT_THREAD_PRIORITY this}
         */
        public Builder threadPriority(int threadPriority) {
            if (taskExecutor != null || taskExecutorForCachedImages != null) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_EXECUTOR);
            }

            if (threadPriority < Thread.MIN_PRIORITY) {
                this.threadPriority = Thread.MIN_PRIORITY;
            } else {
                if (threadPriority > Thread.MAX_PRIORITY) {
                    this.threadPriority = Thread.MAX_PRIORITY;
                } else {
                    this.threadPriority = threadPriority;
                }
            }
            return this;
        }

        /**
         * 当显示一个图像在一个小 {@link android.widget.ImageView ImageView} 后来你想显示
         * 这张图片(从相同的URI)大 {@link android.widget.ImageView ImageView}
         * 因此解码图像的大的大小将缓存在内存中规模较小的前一个解码图像.<br />
         * So <b>默认行为是允许在内存中缓存多个大小的一个形象</b>.
         * 你可以否认它通过调用方法:当一些图像会在内存中缓存然后之前图片缓存的大小(如果它存在的话)将被删除从内存缓存.
         */
        public Builder denyCacheImageMultipleSizesInMemory() {
            this.denyCacheImageMultipleSizesInMemory = true;
            return this;
        }

        /**
         * 设置队列处理任务加载和显示图像的类型<br />
         * 默认值 - {@link QueueProcessingType#FIFO}
         */
        public Builder tasksProcessingOrder(QueueProcessingType tasksProcessingType) {
            if (taskExecutor != null || taskExecutorForCachedImages != null) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_EXECUTOR);
            }

            this.tasksProcessingType = tasksProcessingType;
            return this;
        }

        /**
         * 设置最大内存缓存大小 {@link android.graphics.Bitmap bitmaps} (in bytes).<br />
         * 默认值 - 可用的应用程序内存的1/8<br />
         * <b>注意:</b> 如果你使用这个方法
         * {@link com.cmonbaby.images.cache.memory.impl.LruMemoryCache LruMemoryCache}
         * 将被用作内存缓存。你可以使用 {@link #memoryCache(MemoryCache)} 设置自己的实现方法
         * {@link MemoryCache}.
         */
        public Builder memoryCacheSize(int memoryCacheSize) {
            if (memoryCacheSize <= 0) throw new IllegalArgumentException("memoryCacheSize must be a positive number");

            if (memoryCache != null) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_MEMORY_CACHE);
            }

            this.memoryCacheSize = memoryCacheSize;
            return this;
        }

        /**
         * 设置最大内存缓存大小 (在可用的应用程序内存的百分比) for {@link android.graphics.Bitmap
         * bitmaps}.<br />
         * 默认值 - 可用的应用程序内存的1/8<br />
         * <b>注意:</b> 如果你使用这个方法
         * {@link com.cmonbaby.images.cache.memory.impl.LruMemoryCache LruMemoryCache}
         * 将被用作内存缓存。你可以使用 {@link #memoryCache(MemoryCache)} 设置自己的实现方法
         * {@link MemoryCache}.
         */
        public Builder memoryCacheSizePercentage(int availableMemoryPercent) {
            if (availableMemoryPercent <= 0 || availableMemoryPercent >= 100) {
                throw new IllegalArgumentException("availableMemoryPercent must be in range (0 < % < 100)");
            }

            if (memoryCache != null) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_MEMORY_CACHE);
            }

            long availableMemory = Runtime.getRuntime().maxMemory();
            memoryCacheSize = (int) (availableMemory * (availableMemoryPercent / 100f));
            return this;
        }

        /**
         * 设置内存缓存 {@link android.graphics.Bitmap bitmaps}.<br />
         * 默认值 - {@link com.cmonbaby.images.cache.memory.impl.LruMemoryCache LruMemoryCache}
         * 有限的内存缓存大小(可用应用程序内存的大小= 1/8)<br />
         * <br />
         * <b>注意:</b> 如果你设置自定义内存缓存,那么以下配置选项将不考虑
         * <ul>
         * <li>{@link #memoryCacheSize(int)}</li>
         * </ul>
         */
        public Builder memoryCache(MemoryCache memoryCache) {
            if (memoryCacheSize != 0) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_MEMORY_CACHE);
            }

            this.memoryCache = memoryCache;
            return this;
        }

        /** @deprecated Use {@link #diskCacheSize(int)} instead */
        @Deprecated
        public Builder discCacheSize(int maxCacheSize) {
            return diskCacheSize(maxCacheSize);
        }

        /**
         * 设置图像的最大磁盘高速缓存大小 (in bytes).<br />
         * 默认:磁盘高速缓存是无限的<br />
         * <b>注意:</b> 如果你使用这个方法
         * {@link com.cmonbaby.images.cache.disc.impl.ext.LruDiskCache LruDiskCache}
         * 将被用作磁盘缓存。您可以使用 {@link #diskCache(DiskCache)} 介绍自己的方法
         * implementation of {@link DiskCache}
         */
        public Builder diskCacheSize(int maxCacheSize) {
            if (maxCacheSize <= 0) throw new IllegalArgumentException("maxCacheSize must be a positive number");

            if (diskCache != null) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_DISK_CACHE_PARAMS);
            }

            this.diskCacheSize = maxCacheSize;
            return this;
        }

        /** @deprecated Use {@link #diskCacheFileCount(int)} instead */
        @Deprecated
        public Builder discCacheFileCount(int maxFileCount) {
            return diskCacheFileCount(maxFileCount);
        }

        /**
         * 在磁盘高速缓存目录中设置最大文件数<br />
         * 默认:磁盘高速缓存是无限的<br />
         * <b>注意:</b> 如果你使用这个方法
         * {@link com.cmonbaby.images.cache.disc.impl.ext.LruDiskCache LruDiskCache}
         * 将被用作磁盘缓存。您可以使用 {@link #diskCache(DiskCache)} 介绍自己的方法
         * implementation of {@link DiskCache}
         */
        public Builder diskCacheFileCount(int maxFileCount) {
            if (maxFileCount <= 0) throw new IllegalArgumentException("maxFileCount must be a positive number");

            if (diskCache != null) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_DISK_CACHE_PARAMS);
            }

            this.diskCacheFileCount = maxFileCount;
            return this;
        }

        /** @deprecated Use {@link #diskCacheFileNameGenerator(com.cmonbaby.images.cache.disc.naming.FileNameGenerator)} */
        @Deprecated
        public Builder discCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
            return diskCacheFileNameGenerator(fileNameGenerator);
        }

        /**
         * 在文件缓存的磁盘缓存设置名称生成器<br />
         * 默认值 -
         * {@link com.cmonbaby.images.core.DefaultConfigurationFactory#createFileNameGenerator()
         * DefaultConfigurationFactory.createFileNameGenerator()}
         */
        public Builder diskCacheFileNameGenerator(FileNameGenerator fileNameGenerator) {
            if (diskCache != null) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR);
            }

            this.diskCacheFileNameGenerator = fileNameGenerator;
            return this;
        }

        /** @deprecated Use {@link #diskCache(com.cmonbaby.images.cache.disc.DiskCache)} */
        @Deprecated
        public Builder discCache(DiskCache diskCache) {
            return diskCache(diskCache);
        }

        /**
         * 设置磁盘缓存图像.<br />
         * 默认值 - {@link com.cmonbaby.images.cache.disc.impl.UnlimitedDiskCache
         * UnlimitedDiskCache}. 被定义为缓存目录
         * {@link com.cmonbaby.images.utils.StorageUtils#getCacheDirectory(Context)
         * StorageUtils.getCacheDirectory(Context)}.<br />
         * <br />
         * <b>注意:</b> 如果你设置自定义磁盘高速缓存,那么以下配置选项将不考虑:
         * <ul>
         * <li>{@link #diskCacheSize(int)}</li>
         * <li>{@link #diskCacheFileCount(int)}</li>
         * <li>{@link #diskCacheFileNameGenerator(FileNameGenerator)}</li>
         * </ul>
         */
        public Builder diskCache(DiskCache diskCache) {
            if (diskCacheSize > 0 || diskCacheFileCount > 0) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_DISK_CACHE_PARAMS);
            }
            if (diskCacheFileNameGenerator != null) {
                LogUtils.e(Cons.LOG_TAG, WARNING_OVERLAP_DISK_CACHE_NAME_GENERATOR);
            }

            this.diskCache = diskCache;
            return this;
        }

        /**
         * 设置程序将负责下载图像<br />
         * 默认值 -
         * {@link com.cmonbaby.images.core.DefaultConfigurationFactory#createImageDownloader(Context)
         * DefaultConfigurationFactory.createImageDownloader()}
         */
        public Builder imageDownloader(ImageDownloader imageDownloader) {
            this.downloader = imageDownloader;
            return this;
        }

        /**
         * 设置程序将负责解码的图像流.<br />
         * 默认值 -
         * {@link com.cmonbaby.images.core.DefaultConfigurationFactory#createImageDecoder(boolean)
         * DefaultConfigurationFactory.createImageDecoder()}
         */
        public Builder imageDecoder(ImageDecoder imageDecoder) {
            this.decoder = imageDecoder;
            return this;
        }

        /**
         * 设置默认的 {@linkplain DisplayImageOptions display image options} 为图像显示.
         * 这些选项将用于每一个 {@linkplain ImageLoader#displayImage(String, android.widget.ImageView) 图像显示调用}
         * 没有通过自定义 {@linkplain DisplayImageOptions options}<br />
         * 默认值 - {@link DisplayImageOptions#createSimple() Simple options}
         */
        public Builder defaultDisplayImageOptions(DisplayImageOptions defaultDisplayImageOptions) {
            this.defaultDisplayImageOptions = defaultDisplayImageOptions;
            return this;
        }

        /**
         * 可以详细记录{@link ImageLoader}工作。为了防止详细日志不调用这个方法
         * ImageLoader日志完全(甚至错误日志)
         */
        public Builder writeDebugLogs() {
            this.writeLogs = true;
            return this;
        }

        /** 构造配置 {@link ImageLoaderConfiguration} object */
        public ImageLoaderConfiguration build() {
            initEmptyFieldsWithDefaultValues();
            return new ImageLoaderConfiguration(this);
        }

        private void initEmptyFieldsWithDefaultValues() {
            if (taskExecutor == null) {
                taskExecutor = DefaultConfigurationFactory
                        .createExecutor(threadPoolSize, threadPriority, tasksProcessingType);
            } else {
                customExecutor = true;
            }
            if (taskExecutorForCachedImages == null) {
                taskExecutorForCachedImages = DefaultConfigurationFactory
                        .createExecutor(threadPoolSize, threadPriority, tasksProcessingType);
            } else {
                customExecutorForCachedImages = true;
            }
            if (diskCache == null) {
                if (diskCacheFileNameGenerator == null) {
                    diskCacheFileNameGenerator = DefaultConfigurationFactory.createFileNameGenerator();
                }
                diskCache = DefaultConfigurationFactory
                        .createDiskCache(context, diskCacheFileNameGenerator, diskCacheSize, diskCacheFileCount);
            }
            if (memoryCache == null) {
                memoryCache = DefaultConfigurationFactory.createMemoryCache(context, memoryCacheSize);
            }
            if (denyCacheImageMultipleSizesInMemory) {
                memoryCache = new FuzzyKeyMemoryCache(memoryCache, MemoryCacheUtils.createFuzzyKeyComparator());
            }
            if (downloader == null) {
                downloader = DefaultConfigurationFactory.createImageDownloader(context);
            }
            if (decoder == null) {
                decoder = DefaultConfigurationFactory.createImageDecoder(writeLogs);
            }
            if (defaultDisplayImageOptions == null) {
                defaultDisplayImageOptions = DisplayImageOptions.createSimple();
            }
        }
    }

    /**
     * 装饰。从网络防止下载 (throws {@link IllegalStateException exception}).<br />
     * 在大多数情况下,这个下载器不能直接使用
     */
    private static class NetworkDeniedImageDownloader implements ImageDownloader {

        private final ImageDownloader wrappedDownloader;

        public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) {
            this.wrappedDownloader = wrappedDownloader;
        }

        @Override
        public InputStream getStream(String imageUri, Object extra) throws IOException {
            switch (Scheme.ofUri(imageUri)) {
                case HTTP:
                case HTTPS:
                    throw new IllegalStateException();
                default:
                    return wrappedDownloader.getStream(imageUri, extra);
            }
        }
    }

    /**
     * 装饰。处理这个问题在低速网络使用{@link com.cmonbaby.images.core.assist.FlushedInputStream}.
     */
    private static class SlowNetworkImageDownloader implements ImageDownloader {

        private final ImageDownloader wrappedDownloader;

        public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) {
            this.wrappedDownloader = wrappedDownloader;
        }

        @Override
        public InputStream getStream(String imageUri, Object extra) throws IOException {
            InputStream imageStream = wrappedDownloader.getStream(imageUri, extra);
            switch (Scheme.ofUri(imageUri)) {
                case HTTP:
                case HTTPS:
                    return new FlushedInputStream(imageStream);
                default:
                    return imageStream;
            }
        }
    }

}

